package simulator.srcv6;

import java.util.Vector;

import simulator.srcv6.TxRxEvent.TxRxEventType;


/**
 * 
 * Esta classe implementa o comportamento de um receptor..<br><br>
 * Possuí métodos para:<br>
 * - Iniciar a recepçao de dados {@link Sender.Data}.<br>
 * - Terminar a recepçao de dados {@link Sender.Data}.<br>
 *
 * @author Grupo de Redes do ISCTE (20012/13)
 *
 */

public class Receiver {
	public enum State { WAIT, RX };
	private State state;
	double d;
	static double vp=200000000.0; // vp = 2e8 m/s.
	private Vector<Data> windowReceiver;
	private int windowLimit = 2;
	private int RXindex = 1;
	private int varAuxTest = 0;
	private double probErro;
	private boolean windowHasError = false;
	private Counter count;
	
	public Receiver(double length, Counter count, double probErro, int window) {
		
		this.windowLimit = window;
		this.count = count;
		this.probErro = probErro;
		state = State.WAIT;
		d = length;
		windowReceiver = new Vector<>();
		for (int i = 0; i < windowLimit; i++) {
			windowReceiver.add(new Data(-1, i  , -1));
			windowReceiver.get(i).setCheck(false);
		}
	}
	
	/**
	 * Inicia a recepção de dados.
	 * 
	 * @param      Os dados recebidos.	
	*/
	public void startRX(Data data) {

		
		//nota: provavelmente sera aqui que havera uma prob de a trama ser transmitida com erros
		
		boolean dataTemErro= false;
		
		for (int i = 0; i < data.getSize(); i++) {
			if( Math.random()< probErro){
				dataTemErro = true;
				
				System.out.println("[StartRX] Trama com erro: Id "+data.getID());
				
			}
		}
		
		if(dataTemErro == false)
			receiveFrame(data);
//		else count.frameWithError();
		
		
		state = State.RX;
		
		// Update statistics		
		
		// Output
		String s = "[Receiver@";
		s=s+Simulator.getClock()+" Start RX Data ID: "+data.getID()+"]";
		Simulator.debug(s);	
		
        s=""+Simulator.getClock()+"\t"+"Srx"+"\t"+data.getID()+"\t";
        s=s+data.getTimeStamp()+"\t"+"-"+"\t"+"-"+"\t"+Simulator.getClock()+"\t"+"-"+"\t"+"-"+"\t"+"-";
		Simulator.data(s);		
	}
	
	public void stopRX(Data data) {

//		if(data.getID() == 6)
//			try {
//				Thread.sleep(10000000);
//			} catch (InterruptedException e) {
//				// TODO Auto-generated catch block
//				e.printStackTrace();
//			}
		
		
		state = State.WAIT;
		
		double tprog = this.d /this.vp;
		
		
		
		checkIfACKIsOk();
		if(windowHasError == false){
		//nota: caso a trama n tenha erros sera criado um evento em que a trama tem o id do RXindex
		Data dataACK = new Data(Simulator.getClock(), RXindex, data.getSize()); //nota: em principio o size nao interessa
		TxRxEvent startRXEvent = new TxRxEvent((Simulator.getClock() + tprog ), TxRxEventType.ARRIVAL_ACK, dataACK);
		Simulator.addEvent(startRXEvent);
		
		System.out.println("[STOPRX] um novo evento ARRIVAL_ACK foi agendado");
		
		try {
			Thread.sleep(0);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		}
		
		else{
		windowHasError = false;
		System.out.println("[STOPRX] um erro ocurreu, nenhum ACK sera enviado");
		
		}
		
		try {
			Thread.sleep(0);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		
		// Update statistics
		/* Ao tempo total no sistema, delaySys é adicionado o tempo passado no 
		 * sistema (tempoActual-tempoGeração) da trama que acabou de ser recebida
		 */
		TxRxSystem.delaySys+=Simulator.getClock()-data.getTimeStamp();
		
		// Output
		String s = "[Receiver@";
		s=s+Simulator.getClock()+" Stop RX Data ID (State="+state+"): "+data.getID()+"]";
		Simulator.debug(s);
		
        s=""+Simulator.getClock()+"\t"+"Erx"+"\t"+data.getID()+"\t";
        s=s+data.getTimeStamp()+"\t"+"-"+"\t"+"-"+"\t"+"-"+"\t"+Simulator.getClock()+"\t"+"-"+"\t"+"-";
		Simulator.data(s);		
	}
	
	public void receiveFrame(Data data){
		System.out.println("[Reciver] Start recive frame");
		for (int i = 0; i < windowLimit; i++) {
			System.out.println("[RECEIVER] id da janela receptor: " +windowReceiver.get(i).getID());
			if(windowReceiver.get(i).getID() == data.getID()){
				windowReceiver.get(i).setCheck(true);
				System.out.println("Trama recebida com sucesso, id: "+ data.getID() );
			}
		}
	}
	
	public void checkIfACKIsOk(){
		if(windowReceiver.get(0).isCheck() == false){
			windowHasError = true;
			
			System.out.println("[STOPRX] a trama na posicao 0 tem erro, trama com id: " + windowReceiver.get(0).getID());
		}
		
		else{
			
			for (int i = 0; i < windowLimit; i++) {
				if(windowReceiver.get(i).isCheck() == true){
					RXindex = windowReceiver.get(i).getID() + 1;
					
					System.out.println("[STOPRX] o ACK que sera enviado tera o id: " + RXindex);
					
					try {
						Thread.sleep(0);
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
				else
					break;
			}
			
			
			for (int i = 0; i < windowLimit && !windowReceiver.isEmpty(); i++) {
				if(windowReceiver.get(0).isCheck() == true){
					System.out.println("[STOPRX] a remover trama da janela do receptor com id: "+ windowReceiver.get(0) );
					windowReceiver.remove(0);
					
					try {
						Thread.sleep(0);
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
					
				}
				else
					break;
			}
			
			int varAux = 0;
			
			if(windowReceiver.size() == 0)
				varAux = RXindex;
			else
				varAux = windowReceiver.get(windowReceiver.size()-1).getID() +1;
			
			
			
			for (int i = windowReceiver.size(); i < windowLimit; i++) {
				Data invisData = new Data(-1, varAux, -1);
				invisData.setCheck(false);
				windowReceiver.add(invisData);
				
				
				
				System.out.println("[STOPRX] foi gerada uma nova trama invisivel com o id: " + varAux);
				varAux++;
				
				try {
					Thread.sleep(0);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}
	}
}
